home *** CD-ROM | disk | FTP | other *** search
/ Apple Developer Connection Student Program / ADC Tools Sampler CD Disk 3 1999.iso / Metrowerks CodeWarrior / Java Support / Java_Source / Java2 / src / javax / swing / UIDefaults.java < prev    next >
Encoding:
Java Source  |  1999-05-28  |  16.1 KB  |  518 lines  |  [TEXT/CWIE]

  1. /*
  2.  * @(#)UIDefaults.java    1.28 98/08/28
  3.  *
  4.  * Copyright 1997, 1998 by Sun Microsystems, Inc.,
  5.  * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
  6.  * All rights reserved.
  7.  *
  8.  * This software is the confidential and proprietary information
  9.  * of Sun Microsystems, Inc. ("Confidential Information").  You
  10.  * shall not disclose such Confidential Information and shall use
  11.  * it only in accordance with the terms of the license agreement
  12.  * you entered into with Sun.
  13.  */
  14.  
  15. package javax.swing;
  16.  
  17.  
  18. import javax.swing.plaf.ComponentUI;
  19. import javax.swing.border.Border;
  20. import javax.swing.event.SwingPropertyChangeSupport;
  21.  
  22. import java.util.Hashtable;
  23. import java.awt.Font;
  24. import java.awt.Color;
  25. import java.awt.Insets;
  26. import java.awt.Dimension;
  27. import java.lang.reflect.Method;
  28. import java.beans.PropertyChangeListener;
  29. import java.beans.PropertyChangeEvent;
  30.  
  31.  
  32. /**
  33.  * A table of defaults for Swing components.  Applications can set/get
  34.  * default values via the UIManager.
  35.  * <p>
  36.  * <strong>Warning:</strong>
  37.  * Serialized objects of this class will not be compatible with
  38.  * future Swing releases.  The current serialization support is appropriate
  39.  * for short term storage or RMI between applications running the same
  40.  * version of Swing.  A future release of Swing will provide support for
  41.  * long term persistence.
  42.  *
  43.  * @see UIManager
  44.  * @version 1.28 08/28/98
  45.  * @author Hans Muller
  46.  */
  47. public class UIDefaults extends Hashtable
  48. {
  49.     private static final Object PENDING = new String("Pending");
  50.  
  51.     private SwingPropertyChangeSupport changeSupport;
  52.  
  53.  
  54.     /**
  55.      * Create an empty defaults table.
  56.      */
  57.     public UIDefaults() {
  58.         super();
  59.     }
  60.  
  61.  
  62.     /**
  63.      * Create a defaults table initialized with the specified
  64.      * key/value pairs.  For example:
  65.      * <pre>
  66.         Object[] uiDefaults = {
  67.              "Font", new Font("Dialog", Font.BOLD, 12),
  68.             "Color", Color.red,
  69.              "five", new Integer(5)
  70.         }
  71.         UIDefaults myDefaults = new UIDefaults(uiDefaults);
  72.      * </pre>
  73.      */
  74.     public UIDefaults(Object[] keyValueList) {
  75.         super(keyValueList.length / 2);
  76.         for(int i = 0; i < keyValueList.length; i += 2) {
  77.             super.put(keyValueList[i], keyValueList[i + 1]);
  78.         }
  79.     }
  80.  
  81.  
  82.     /**
  83.      * Returns the value for key.  If the value is a
  84.      * <code>UIDefaults.LazyValue</code> then the real
  85.      * value is computed with <code>LazyValue.createValue()</code>,
  86.      * the table entry is replaced, and the real value is returned.
  87.      * If the value is an <code>UIDefaults.ActiveValue</code>
  88.      * the table entry is not replaced - the value is computed
  89.      * with ActiveValue.createValue() for each get() call.
  90.      *
  91.      * @see LazyValue
  92.      * @see ActiveValue
  93.      * @see java.util.Hashtable#get
  94.      */
  95.     public Object get(Object key)
  96.     {
  97.         /* Quickly handle the common case, without grabbing
  98.          * a lock.
  99.          */
  100.         Object value = super.get(key);
  101.         if ((value != PENDING) &&
  102.             !(value instanceof ActiveValue) &&
  103.             !(value instanceof LazyValue)) {
  104.             return value;
  105.         }
  106.  
  107.         /* If the LazyValue for key is being constructed by another
  108.          * thread then wait and then return the new value, otherwise drop
  109.          * the lock and construct the ActiveValue or the LazyValue.
  110.          * We use the special value PENDING to mark LazyValues that
  111.          * are being constructed.
  112.          */
  113.         synchronized(this) {
  114.             value = super.get(key);
  115.             if (value == PENDING) {
  116.                 do {
  117.                     try {
  118.                         this.wait();
  119.                     }
  120.                     catch (InterruptedException e) {
  121.                     }
  122.                     value = super.get(key);
  123.                 }
  124.                 while(value == PENDING);
  125.                 return value;
  126.             }
  127.             else if (value instanceof LazyValue) {
  128.                 super.put(key, PENDING);
  129.             }
  130.             else if (!(value instanceof ActiveValue)) {
  131.                 return value;
  132.             }
  133.         }
  134.  
  135.         /* At this point we know that the value of key was
  136.          * a LazyValue or an ActiveValue.
  137.          */
  138.         if (value instanceof LazyValue) {
  139.             try {
  140.                 /* If an exception is thrown we'll just put the LazyValue
  141.                  * back in the table.
  142.                  */
  143.                 value = ((LazyValue)value).createValue(this);
  144.             }
  145.             finally {
  146.                 synchronized(this) {
  147.                     if (value == null) {
  148.                         super.remove(key);
  149.                     }
  150.                     else {
  151.                         super.put(key, value);
  152.                     }
  153.                     this.notify();
  154.                 }
  155.             }
  156.         }
  157.         else {
  158.             value = ((ActiveValue)value).createValue(this);
  159.         }
  160.  
  161.         return value;
  162.     }
  163.  
  164.  
  165.     /**
  166.      * Set the value of <code>key</code> to <code>value</code>.
  167.      * If <code>key</code> is a string and the new value isn't
  168.      * equal to the old one, fire a PropertyChangeEvent.  If value
  169.      * is null, the key is removed from the table.
  170.      *
  171.      * @param key    the unique Object who's value will be used to 
  172.      *               retreive the data value associated with it
  173.      * @param value  the new Object to store as data under that key
  174.      * @return the previous Object value, or null
  175.      * @see #putDefaults
  176.      * @see java.util.Hashtable#put
  177.      */
  178.     public Object put(Object key, Object value) {
  179.         Object oldValue = (value == null) ? super.remove(key) : super.put(key, value);
  180.         if (key instanceof String) {
  181.             firePropertyChange((String)key, oldValue, value);
  182.         }
  183.         return oldValue;
  184.     }
  185.  
  186.  
  187.     /**
  188.      * Put all of the key/value pairs in the database and
  189.      * unconditionally generate one PropertyChangeEvent.
  190.      * The events oldValue and newValue will be null and its
  191.      * propertyName will be "UIDefaults".
  192.      *
  193.      * @see #put
  194.      * @see java.util.Hashtable#put
  195.      */
  196.     public void putDefaults(Object[] keyValueList) {
  197.         for(int i = 0; i < keyValueList.length; i += 2) {
  198.             Object value = keyValueList[i + 1];
  199.             if (value == null) {
  200.                 super.remove(keyValueList[i]);
  201.             }
  202.             else {
  203.                 super.put(keyValueList[i], value);
  204.             }
  205.         }
  206.         firePropertyChange("UIDefaults", null, null);
  207.     }
  208.  
  209.  
  210.     /**
  211.      * If the value of <code>key</code> is a Font return it, otherwise
  212.      * return null.
  213.      */
  214.     public Font getFont(Object key) {
  215.         Object value = get(key);
  216.         return (value instanceof Font) ? (Font)value : null;
  217.     }
  218.  
  219.     /**
  220.      * If the value of <code>key</code> is a Color return it, otherwise
  221.      * return null.
  222.      */
  223.     public Color getColor(Object key) {
  224.         Object value = get(key);
  225.         return (value instanceof Color) ? (Color)value : null;
  226.     }
  227.  
  228.  
  229.     /**
  230.      * If the value of <code>key</code> is an Icon return it, otherwise
  231.      * return null.
  232.      */
  233.     public Icon getIcon(Object key) {
  234.         Object value = get(key);
  235.         return (value instanceof Icon) ? (Icon)value : null;
  236.     }
  237.  
  238.  
  239.     /**
  240.      * If the value of <code>key</code> is a Border return it, otherwise
  241.      * return null.
  242.      */
  243.     public Border getBorder(Object key) {
  244.         Object value = get(key);
  245.         return (value instanceof Border) ? (Border)value : null;
  246.     }
  247.  
  248.  
  249.     /**
  250.      * If the value of <code>key</code> is a String return it, otherwise
  251.      * return null.
  252.      */
  253.     public String getString(Object key) {
  254.         Object value = get(key);
  255.         return (value instanceof String) ? (String)value : null;
  256.     }
  257.  
  258.     /**
  259.      * If the value of <code>key</code> is a Integer return its
  260.      * integer value, otherwise return 0.
  261.      */
  262.     public int getInt(Object key) {
  263.         Object value = get(key);
  264.         return (value instanceof Integer) ? ((Integer)value).intValue() : 0;
  265.     }
  266.  
  267.     /**
  268.      * If the value of <code>key</code> is a Insets return it, otherwise
  269.      * return null.
  270.      */
  271.     public Insets getInsets(Object key) {
  272.         Object value = get(key);
  273.         return (value instanceof Insets) ? (Insets)value : null;
  274.     }
  275.  
  276.     /**
  277.      * If the value of <code>key</code> is a Dimension return it, otherwise
  278.      * return null.
  279.      */
  280.     public Dimension getDimension(Object key) {
  281.         Object value = get(key);
  282.         return (value instanceof Dimension) ? (Dimension)value : null;
  283.     }
  284.  
  285.  
  286.     /**
  287.      * The value of get(uidClassID) must be the String name of a
  288.      * class that implements the corresponding ComponentUI
  289.      * class.  If the class hasn't been loaded before, this method looks 
  290.      * up the class with <code>uiClassLoader.loadClass()</code> if a non null
  291.      * class loader is provided, <code>classForName()</code> otherwise.
  292.      * <p>
  293.      * If a mapping for uiClassID exists or if the specified
  294.      * class can't be found, return null.
  295.      * <p>
  296.      * This method is used by <code>getUI</code>, it's usually
  297.      * not neccessary to call it directly.
  298.      *
  299.      * @return The value of <code>Class.forName(get(uidClassID))</code>.
  300.      * @see #getUI
  301.      */
  302.     public Class getUIClass(String uiClassID, ClassLoader uiClassLoader)
  303.     {
  304.         try {
  305.             String className = (String)get(uiClassID);
  306.             Class cls = (Class)get(className);
  307.             if (cls == null) {
  308.         if (uiClassLoader == null) {
  309.             cls = Class.forName(className);
  310.         }
  311.         else {
  312.             cls = uiClassLoader.loadClass(className);
  313.         }
  314.                 if (cls != null) {
  315.                     // Save lookup for future use, as forName is slow.
  316.                     put(className, cls);
  317.                 }
  318.             }
  319.             return cls;
  320.         } 
  321.     catch (ClassNotFoundException e) {
  322.             return null;
  323.         } 
  324.     catch (ClassCastException e) {
  325.             return null;
  326.         }
  327.     }
  328.  
  329.  
  330.     /**
  331.      * Returns the L&F class that renders this component.
  332.      *
  333.      * @return the Class object returned by getUIClass(uiClassID, null)
  334.      */
  335.     public Class getUIClass(String uiClassID) {
  336.     return getUIClass(uiClassID, null);
  337.     }
  338.  
  339.  
  340.     /**
  341.      * If getUI() fails for any reason, it calls this method before
  342.      * returning null.  Subclasses may choose to do more or
  343.      * less here.
  344.      *
  345.      * @param msg Message string to print.
  346.      * @see #getUI
  347.      */
  348.     protected void getUIError(String msg) {
  349.         System.err.println("UIDefaults.getUI() failed: " + msg);
  350.         try {
  351.             throw new Error();
  352.         }
  353.         catch (Throwable e) {
  354.             e.printStackTrace();
  355.         }
  356.     }
  357.  
  358.     /**
  359.      * Create an ComponentUI implementation for the
  360.      * specified component.  In other words create the look
  361.      * and feel specific delegate object for <code>target</code>.
  362.      * This is done in two steps:
  363.      * <ul>
  364.      * <li> Lookup the name of the ComponentUI implementation
  365.      * class under the value returned by target.getUIClassID().
  366.      * <li> Use the implementation classes static <code>createUI()</code>
  367.      * method to construct a look and feel delegate.
  368.      * </ul>
  369.      */
  370.     public ComponentUI getUI(JComponent target)
  371.     {
  372.     ClassLoader uiClassLoader = target.getClass().getClassLoader();
  373.         Class uiClass = getUIClass(target.getUIClassID(), uiClassLoader);
  374.         Object uiObject = null;
  375.  
  376.         if (uiClass == null) {
  377.             getUIError("no ComponentUI class for: " + target);
  378.         }
  379.         else {
  380.             try {
  381.         Method m = (Method)get(uiClass);
  382.         if (m == null) {
  383.             Class acClass = javax.swing.JComponent.class;
  384.             m = uiClass.getMethod("createUI", new Class[]{acClass});
  385.             put(uiClass, m);
  386.         }
  387.         uiObject = m.invoke(null, new Object[]{target});
  388.             }
  389.             catch (NoSuchMethodException e) {
  390.                 getUIError("static createUI() method not found in " + uiClass);
  391.             }
  392.             catch (Exception e) {
  393.                 getUIError("createUI() failed for " + target + " " + e);
  394.             }
  395.         }
  396.  
  397.         return (ComponentUI)uiObject;
  398.     }
  399.  
  400.     /**
  401.      * Add a PropertyChangeListener to the listener list.
  402.      * The listener is registered for all properties.
  403.      * <p>
  404.      * A PropertyChangeEvent will get fired whenever a default
  405.      * is changed.
  406.      *
  407.      * @param listener  The PropertyChangeListener to be added
  408.      * @see java.beans.PropertyChangeSupport
  409.      */
  410.     public synchronized void addPropertyChangeListener(PropertyChangeListener listener) {
  411.         if (changeSupport == null) {
  412.             changeSupport = new SwingPropertyChangeSupport(this);
  413.         }
  414.         changeSupport.addPropertyChangeListener(listener);
  415.     }
  416.  
  417.  
  418.     /**
  419.      * Remove a PropertyChangeListener from the listener list.
  420.      * This removes a PropertyChangeListener that was registered
  421.      * for all properties.
  422.      *
  423.      * @param listener  The PropertyChangeListener to be removed
  424.      * @see java.beans.PropertyChangeSupport
  425.      */
  426.     public synchronized void removePropertyChangeListener(PropertyChangeListener listener) {
  427.         if (changeSupport != null) {
  428.             changeSupport.removePropertyChangeListener(listener);
  429.         }
  430.     }
  431.  
  432.  
  433.     /**
  434.      * Support for reporting bound property changes.  If oldValue and
  435.      * newValue are not equal and the PropertyChangeEvent listener list
  436.      * isn't empty, then fire a PropertyChange event to each listener.
  437.      *
  438.      * @param propertyName  The programmatic name of the property that was changed.
  439.      * @param oldValue  The old value of the property.
  440.      * @param newValue  The new value of the property.
  441.      * @see java.beans.PropertyChangeSupport
  442.      */
  443.     protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
  444.         if (changeSupport != null) {
  445.             changeSupport.firePropertyChange(propertyName, oldValue, newValue);
  446.         }
  447.     }
  448.  
  449.  
  450.     /**
  451.      * This class enables one to store an entry in the defaults
  452.      * table that isn't constructed until the first time it's
  453.      * looked up with one of the <code>getXXX(key)</code> methods.
  454.      * Lazy values are useful for defaults that are expensive
  455.      * to construct or are seldom retrieved.  The first time
  456.      * a LazyValue is retrieved its "real value" is computed
  457.      * by calling <code>LazyValue.createValue()</code> and the real
  458.      * value is used to replace the LazyValue in the UIDefaults
  459.      * table.  Subsequent lookups for the same key return
  460.      * the real value.  Here's an example of a LazyValue that
  461.      * constructs a Border:
  462.      * <pre>
  463.      *  Object borderLazyValue = new UIDefaults.LazyValue() {
  464.      *      public Object createValue(UIDefaults table) {
  465.      *          return new BorderFactory.createLoweredBevelBorder();
  466.      *      }
  467.      *  };
  468.      *
  469.      *  uiDefaultsTable.put("MyBorder", borderLazyValue);
  470.      * </pre>
  471.      *
  472.      * @see UIDefaults#get
  473.      */
  474.     public interface LazyValue {
  475.         /**
  476.          * Creates the actual value retrieved from the UIDefaults
  477.          * table. When an object that implements this interface is
  478.          * retrieved from the table, this method is used to create
  479.          * the real value, which is then stored in the table and
  480.          * returned to the calling method.
  481.          *
  482.          * @param table  a UIDefaults table
  483.          * @return the created Object 
  484.          */
  485.         Object createValue(UIDefaults table);
  486.     }
  487.  
  488.  
  489.     /**
  490.      * This class enables one to store an entry in the defaults
  491.      * table that's constructed each time it's looked up with one of
  492.      * the <code>getXXX(key)</code> methods. Here's an example of
  493.      * an ActiveValue that constructs a DefaultListCellRenderer
  494.      * <pre>
  495.      *  Object cellRendererActiveValue = new UIDefaults.ActiveValue() {
  496.      *      public Object createValue(UIDefaults table) {
  497.      *          return new DefaultListCellRenderer();
  498.      *      }
  499.      *  };
  500.      *
  501.      *  uiDefaultsTable.put("MyRenderer", cellRendererActiveValue);
  502.      * </pre>
  503.      *
  504.      * @see UIDefaults#get
  505.      */
  506.     public interface ActiveValue {
  507.         /**
  508.          * Creates the value retrieved from the UIDefaults table.
  509.          * The object is created each time it is accessed.
  510.          *
  511.          * @param table  a UIDefaults table
  512.          * @return the created Object 
  513.          */
  514.         Object createValue(UIDefaults table);
  515.     }
  516. }
  517.  
  518.